package com.cloud.activiti.service.impl;

import com.cloud.activiti.service.ActivitiExecuteService;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import javax.xml.stream.XMLInputFactory;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Map;

/**
 * activit流程引擎为用户提供了好几种服务及api，用于查询和操作流程，
 * 通过ProcessEngine的getXXXService方法可以获取。
 * 现在对几个接下来会用得比较多的进行介绍：
 *
 * RepositoryService：主要用于查询和操作与部署有关的api。
 * 在流程部署的章节中，就是通过这个服务进行流程部署的；
 * RuntimeService：流程实例的操作与查询。
 * 包括流程实例的查询、启动等；
 * TaskService：任务的查询、完成、设置处理人等；
 * HistoryService：历史流程实例、活动实例、任务实例等查询与删除；
 * ManagementService：命令和job执行。
 * 由于activiti采用命令模式，实质上很多流程引擎的操作最后是封装成命令执行的。
 * 用户也可以自行去调用已有的命令或自定义命令。
 * job主要是和定时任务、异步执行有关。
 */
@Component
public class ActivitiExecuteServiceImpl implements ActivitiExecuteService {

    @Autowired
    RepositoryService repositoryService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;
    @Autowired
    HistoryService historyService;
    @Autowired
    FormService formService;

    /**
     * 流程部署
     *
     * @param classpathResource //创建一个部署的构造器从类路径中添加资源
     * @param name              //设置部署的名称
     * @param category          //设置部署的类别
     * @return
     */
    @Override
    public String deploy(String classpathResource, String name, String category) {
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(classpathResource)
                .name(name)
                .category(category)
                .deploy();
        return deployment.getId();
    }

    /**
     * 开启流程实例
     *
     * @param processDefinitionKey
     * @param businessKey
     * @param variables
     * @return
     */
    @Override
    public String startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
        return processInstance.getId();
    }

    /**
     * 完成任务并且推送任务往下一个节点
     *
     * @param taskId
     * @param map
     */
    @Override
    public void completeTask(String taskId, Map<String, Object> map) {
        taskService.complete(taskId, map);
        System.out.println("当前任务已执行完");
    }

    /**
     * 生成流程图
     * 获取历史流程实例
     * 根据流程定义获取输入流
     *
     * @param processInstanceId
     * @throws Exception
     */
    public void queryProImg(String processInstanceId, String filePath) throws Exception {
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        InputStream is = repositoryService.getProcessDiagram(processInstance.getProcessDefinitionId());
        BufferedImage bi = ImageIO.read(is);
        File file = new File(filePath);
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream fos = new FileOutputStream(file);
        ImageIO.write(bi, "png", fos);
        fos.close();
        is.close();
        System.out.println("图片生成成功");
    }

    /**
     * 通过编码发布流程图
     */
    @Override
    public void deployAddBpmnModel() {
        SequenceFlow flow1 = new SequenceFlow();
        flow1.setId("flow1");
        flow1.setName("start->userTask1");
        flow1.setSourceRef("startEvent1");
        flow1.setTargetRef("userTask1");
        SequenceFlow flow2 = new SequenceFlow();
        flow2.setId("flow2");
        flow2.setName("userTask1->end");
        flow2.setSourceRef("userTask1");
        flow2.setTargetRef("endEvent1");
        BpmnModel bpmnModel = new BpmnModel();
        Process process = new Process();
        process.setId("secondBpmProcess");
        StartEvent startEvent = new StartEvent();
        startEvent.setId("startEvent1");
        startEvent.setOutgoingFlows(Arrays.asList(flow1));
        UserTask userTask1 = new UserTask();
        userTask1.setId("userTask1");
        userTask1.setName("userTask1");
        userTask1.setIncomingFlows(Arrays.asList(flow1));
        userTask1.setOutgoingFlows(Arrays.asList(flow2));
        EndEvent endEvent = new EndEvent();
        endEvent.setId("endEvent1");
        endEvent.setName("endEvent1");
        endEvent.setIncomingFlows(Arrays.asList(flow2));
        process.addFlowElement(startEvent);
        process.addFlowElement(flow1);
        process.addFlowElement(userTask1);
        process.addFlowElement(flow2);
        process.addFlowElement(endEvent);
        bpmnModel.addProcess(process);
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        deploymentBuilder.addBpmnModel("second", bpmnModel);
        deploymentBuilder.deploy();
    }
    /**
     * 通过编码发布流程图
     */
    @Override
    public void convertBpmnModel() throws Exception {
        InputStream inputStream = new FileInputStream("");
        BpmnXMLConverter converter = new BpmnXMLConverter();
        XMLInputFactory factory = XMLInputFactory.newInstance();
        BpmnModel bpmnModel = converter.convertToBpmnModel(factory.createXMLStreamReader(inputStream));
        Process process = bpmnModel.getMainProcess();
        ServiceTask hookTask = new ServiceTask();
        hookTask.setName("");
        hookTask.setId("");
        process.addFlowElement(hookTask);
        // 修改流程图
//        new BpmnAutoLayout(bpmnModel).execute();
        Files.write(Paths.get(""),converter.convertToXML(bpmnModel));

    }
    @Override
    public String submitStartFormData(String processDefinitionId, String businessKey, Map<String, String> properties) {
        ProcessInstance processInstance = formService.submitStartFormData(processDefinitionId, businessKey, properties);
        return processInstance.getId();
    }

    @Override
    public void submitTaskFormData(String taskId, Map<String, String> properties) {
        formService.submitTaskFormData(taskId,properties);
    }

    @Override
    public void saveFormData(String taskId, Map<String, String> properties) {
        formService.saveFormData(taskId,properties);
    }
}
